/*
 * jQuery djangollery plugin
 *
 * Copyright (c) 2009 Fidel Cornelis
 * Licensed under the MIT License:
 *   http://www.opensource.org/licenses/mit-license.php
 *
 */
(function($) {

    $.fn.djangollery = function( t, o) {
        return this.each(function() {
            new $gl(this,t, o);
        });
    };

	// Write noscript style
	document.write("<style type='text/css'>.noscript{display:none}</style>");

	var ver = 'djangollery-1.0';
	var galleryOffset = 0;
	var galleries = [];
	var allImages = [];	
	var historyCurrentHash;
	var historyBackStack;
	var historyForwardStack;
	var isFirst = false;
	var dontCheck = false;
	var isInitialized = false;

	function getHashFromString(hash) {
		if (!hash) return -1;
		hash = hash.replace(/^.*#/, '');
		if (isNaN(hash)) return -1;
		return (+hash);
	}

	function getHash() {
		var hash = location.hash;
		return getHashFromString(hash);
	}

	function registerGallery(gallery) {
		galleries.push(gallery);

		// update the global offset value
		galleryOffset += gallery.data.length;
	}

	function getGallery(hash) {
		for (i = 0; i < galleries.length; i++) {
			var gallery = galleries[i];
			if (hash < (gallery.data.length+gallery.offset))
				return gallery;
		}
		return 0;
	}
	
	function getIndex(gallery, hash) {
		return hash-gallery.offset;
	}
	
	function clickHandler(e, gallery, link) {
		gallery.pause();
		if (!gallery.settings.enableHistory) {
			var hash = getHashFromString(link.href);
			if (hash >= 0) {
				var index = getIndex(gallery, hash);
				if (index >= 0)
					gallery.goTo(index);
			}
			e.preventDefault();
		}
	}

	function historyCallback() {
		// Using present location.hash always (seems to work, unlike the hash argument passed to this callback)
		var hash = getHash();
		if (hash < 0) return;

		var gallery = getGallery(hash);
		if (!gallery) return;
		
		var index = hash-gallery.offset;
		gallery.goTo(index);
	}
	
	function historyInit() {
		if (isInitialized) return;
		isInitialized = true; 

		var current_hash = location.hash; //(enableHistory) ? location.hash : currentIndexHash; // Ghismo

		historyCurrentHash = current_hash;
		if ($.browser.msie) {
			// To stop the callback firing twice during initilization if no hash present
			if (historyCurrentHash == '') {
				historyCurrentHash = '#';
			}
		} else if ($.browser.safari) {
			// etablish back/forward stacks
			historyBackStack = [];
			historyBackStack.length = history.length;
			historyForwardStack = [];
			isFirst = true;
		}

		setInterval(function() { historyCheck(); }, 100);
	}
	
	function historyAddHistory(hash) {
		// This makes the looping function do something
		historyBackStack.push(hash);
		historyForwardStack.length = 0; // clear forwardStack (true click occured)
		isFirst = true;
	}
	
	function historyCheck() {
		if ($.browser.safari) {
			if (!dontCheck) {
				var historyDelta = history.length - historyBackStack.length;
				
				if (historyDelta) { // back or forward button has been pushed
					isFirst = false;
					if (historyDelta < 0) { // back button has been pushed
						// move items to forward stack
						for (var i = 0; i < Math.abs(historyDelta); i++) historyForwardStack.unshift(historyBackStack.pop());
					} else { // forward button has been pushed
						// move items to back stack
						for (var i = 0; i < historyDelta; i++) historyBackStack.push(historyForwardStack.shift());
					}
					var cachedHash = historyBackStack[historyBackStack.length - 1];
					if (cachedHash != undefined) {
						historyCurrentHash = location.hash; // (enableHistory) ? location.hash : currentIndexHash; // Ghismo
						historyCallback();
					}
				} else if (historyBackStack[historyBackStack.length - 1] == undefined && !isFirst) {
					historyCallback();
					isFirst = true;
				}
			}
		} else {
			// otherwise, check for location.hash
			var current_hash = location.hash; // (enableHistory) ? location.hash : currentIndexHash; // Ghismo
			if(current_hash != historyCurrentHash) {
				historyCurrentHash = current_hash;
				historyCallback();
			}
		}
	}

	var defaults = {
		delay:                  3000,
		preloadAhead:           40, // Set to -1 to preload all images
		enableTopPager:         false,
		enableBottomPager:      true,
		imageContainerSel:      '',
		captionContainerSel:    '',
		controlsContainerSel:   '',
		loadingContainerSel:    '',
		renderSSControls:       true,
		renderNavControls:      true,
		playLinkText:           'Play',
		pauseLinkText:          'Pause',
		prevLinkText:           'Previous',
		nextLinkText:           'Next',
		nextPageLinkText:       'Next &rsaquo;',
		prevPageLinkText:       '&lsaquo; Prev',
		showText:				'Show',
		enableHistory:          false,
		autoStart:              false,
		onChange:               undefined, // accepts a delegate like such: function(prevIndex, nextIndex) { ... }
		onTransitionOut:        undefined, // accepts a delegate like such: function(callback) { ... }
		onTransitionIn:         undefined, // accepts a delegate like such: function() { ... }
		onPageTransitionOut:    undefined, // accepts a delegate like such: function(callback) { ... }
		onPageTransitionIn:     undefined,  // accepts a delegate like such: function() { ... }
        start: 1,
		auto: 0,
        offset: 1,
		scroll: 3,
	    visible: null,
		animation: 'normal',
		buttonNextEvent: 'click',
        buttonPrevEvent: 'click',
        buttonNextCallback: null,
        buttonPrevCallback: null,
		wrap: null
	};

	$.djangollery = function(galleryContainer,thumbsContainerSel, settings) {
		// Now initialize the gallery
		this.settings = $.extend({}, defaults, settings);
        
        this.locked     = false;
        this.$carouselContainer  = null;
        this.clip       = null;
        this.list       = null;
        this.buttonNext = null;
        this.buttonPrev = null;
		//enableHistory = this.settings.enableHistory; // Ghismo

		if (this.interval)
			clearInterval(this.interval);

		this.interval = 0;
		
		this.$galleryContainer=$(galleryContainer);
		this.$galleryContainer.addClass('djangollery-content');
		this.setupSlideshow();
		this.setupCaption();
		this.setupLoader();
		// Setup the jQuery object holding each container that will be transitioned
		this.$transitionContainers = $([]);
		if (this.$imageContainer)
			this.$transitionContainers = this.$transitionContainers.add(this.$imageContainer);
		// if (this.$captionContainer)
		// 		this.$transitionContainers = this.$transitionContainers.add(this.$captionContainer);
		
		// Set the hash index offset for this gallery
		this.offset = galleryOffset;
                
		this.setupThumbs(thumbsContainerSel);
		this.setupCaruosel();
		this.setupShowHide();

		// Add this gallery to the global galleries array
		registerGallery(this);

		this.currentIndex = 0;
		var self = this;

		// Hide the loadingContainer
		if (this.$loadingContainer)
			this.$loadingContainer.hide();

		// Setup controls
		this.setupControls();
        
		// Initialize history only once when the first gallery on the page is initialized
		historyInit();
		
		// Build image
		var hash = getHash();
		var hashGallery = (hash >= 0) ? getGallery(hash) : 0;
		var gotoIndex = (hashGallery && this == hashGallery) ? (hash-this.offset) : 0;
		this.goTo(gotoIndex);

		if (this.settings.autoStart) {
			
			setTimeout(function() { self.play(); }, this.settings.delay);
		}

		// Kickoff Image Preloader after 1 second
		setTimeout(function() { self.preloadInit(); }, 1000);
        
        // For whatever reason, .show() does not work in Safari...
        this.$carouselContainer.css('display', 'block');
        this.buttonNext.css('display', 'block');
        this.buttonPrev.css('display', 'block');

        this.funcNext   = function() { self.next(); };
        this.funcPrev   = function() { self.prev(); };
        this.funcResize = function() { self.reload(); };
        if ($.browser.safari) {
            this.buttons(false, false);
            $(window).bind('load', function() { self.initCarousel(); });
        } else
            this.initCarousel();
		return this;
	};
    

    // Create shortcut for internal use
    var $gl = $.djangollery;

    $gl.fn = $gl.prototype = {
        djangollery: '1.0'
    };

    $gl.fn.extend = $gl.extend = $.extend;

    //  Extend 	 Object
    $gl.fn.extend({
			ver: function() {
				return ver;
			},

			setupThumbs: function(thumbsContainerSel) {				
				this.data = [];
				var gallery = this;
				var whList = 0;
				this.$thumbsContainer = $(thumbsContainerSel);
				this.$thumbsContainer.addClass('djangollery-navigation');
				this.list = this.$thumbsContainer.find('ul.djangollery-thumbs');
                
                this.list.children('li').each(function(i) {
					var $li = $(this);
					var $aThumb = $li.find('a.djangollery-thumb');
					var hash = gallery.offset+i;
                    gallery.format(this, i);
                    var wh=this.offsetWidth+$gl.margin($li, 'marginLeft') + $gl.margin($li, 'marginRight');
                    whList = whList + wh;
                    $li.data('dimension',wh);
					gallery.data.push({
						title:$aThumb.attr('title'),
						slideUrl:$aThumb.attr('href'),
						caption:$li.find('.djangollery-caption').remove(),
						hash:hash
					});
                    
					// Setup history
					$aThumb.attr('rel', 'history');
					$aThumb.attr('href', '#'+hash);
					$aThumb.click(function(e) {
						clickHandler(e, gallery, this);
					});
				});
                this.list.css('width',whList+'px');
				this.list.data('dimension',whList);
				return this;
			},
			/**
			 * Prepara el carousel para mostrar los thumbs
			 * @private
			 * @type Djangollery
			 */	
			setupCaruosel:function() {
				this.$carouselContainer = this.list.parent();
                if (this.$carouselContainer.hasClass('carousel-clip')) {
                    if (!this.$carouselContainer.parent().hasClass('carousel-container'))
                        this.$carouselContainer = this.$carouselContainer.wrap('<div></div>');
                    this.$carouselContainer = this.$carouselContainer.parent();
                } 
                else if (!this.$carouselContainer.hasClass('carousel-container')) {
                    this.$carouselContainer = this.list.wrap('<div></div>').parent();
                }
                
                this.clip = this.list.parent();
                if (!this.clip.length || !this.clip.hasClass('carousel-clip'))
                    this.clip = this.list.wrap('<div></div>').parent();

                this.buttonPrev = $('.carousel-prev', this.$thumbsContainer);

                if (this.buttonPrev.size() == 0 )
                    this.buttonPrev = this.clip.before('<div></div>').prev();

                this.buttonPrev.addClass('carousel-prev');

                this.buttonNext = $('.carousel-next', this.$thumbsContainer);

                if (this.buttonNext.size() == 0 )
                    this.buttonNext = this.clip.before('<div></div>').prev();

                this.buttonNext.addClass('carousel-next');

                this.clip.addClass('carousel-clip');
                this.$carouselContainer.addClass('carousel-container');	
                
			},
			
			/**
			*  Inicializa la cabecera que muestra y oculta  el carousel
			*/
			setupShowHide: function(args) {
				var self = this;
				this.$thumbsContainer.wrap('<div id="djangollery-top"></div>');
				this.$topContainer=$('#djangollery-top');
				
				var $ul=$('<ul></ul>');
				$ul.append('<li class="djangollery-showhide-left"></li>');
				var $anchorShowHide=$('<p >'+this.settings.showText+'</p>');
				var self=this;
				
				$ul.append( $('<li></li>').append($anchorShowHide) );
				var $showhide=$('<div id="djangollery-showhide"></div>')
				$showhide.append($ul);
				
				this.$topContainer.append($showhide);
				this.$topContainer.css('top',( -1 * self.$thumbsContainer.height() ) +'px');
				// Definimos el evento que muestra las thumbs
				this.animateThumbs = false;
				this.thumbsIsShow = false;	
				// $anchorShowHide.click(function() {
				// 	if(! self.animateThumbs) {
				// 		self.animateThumbs=true;
				// 		var newTop = '0';
				// 		if( self.thumbsIsShow ) {
				// 			newTop = ( -1 * self.$thumbsContainer.height() ) +'px';
				// 		}			 
				// 		self.$topContainer.animate({ 
				// 		        top: newTop
				// 			 }, 
				// 			200,
				// 			"swing",
				// 			function() {
				// 				self.thumbsIsShow = !self.thumbsIsShow ;
				// 				self.animateThumbs = false;	
				// 			}
				// 		);
				// 	}					
				// });
				this.$topContainer.hover(
					function() {
						if( !self.thumbsIsShow ) {
							if( self.animateThumbs ) {
								self.$topContainer.stop();
							}
							self.animateThumbs=true;
							self.$topContainer.animate(
								{ top: 0 }, 
								200,
								"swing",
								function() {
									self.animateThumbs = false;	
									self.thumbsIsShow = true;
								}
							);
						}
					},
					function(){
						if( self.thumbsIsShow ) {
							if( self.animateThumbs ) {
								self.$topContainer.stop();
							}
							self.animateThumbs=true;
							self.$topContainer.animate(
								{ top: ( -1 * self.$thumbsContainer.height() ) +'px' }, 
								200,
								"swing",
								function() {
									self.animateThumbs = false;	
									self.thumbsIsShow = false;
								}
							);
						}
					}
				);
				
				
			},
			
			
			
			/**
			 * Prepara el contenedor de la imagen
			 * @private
			 * @type Djangollery
			 */									
			setupSlideshow: function () {
				// Si  el contenedor esta definido en la configuracion
				if (this.settings.imageContainerSel) {
					this.$imageContainer = $(this.settings.imageContainerSel);
					this.$imageContainer.addClass('djangollery-slideshow');
				}
			},
			/**
			 * Prepara el contenedor de la descricion
			 * @private
			 * @type Djangollery
			 */			
			setupCaption: function() {
				// Si  el contenedor esta definido en la configuracion
				if (this.settings.captionContainerSel)  {
					this.$captionContainer = $(this.settings.captionContainerSel);
					this.$captionContainer.addClass('djangollery-caption-container');
					this.$captionContainer.wrap('<div class="djangollery-caption-title"/>');
					this.$captionContainer.hide();
				}
					
			},
			
			/**
			 * Describe what this method does
			 * @private
			 * @type String|Object|Array|Boolean|Number
			 */					
			setupLoader:function() {
				// Preparamos el contendor del loader
				if (this.settings.loadingContainerSel) {
					this.$loadingContainer = $(this.settings.loadingContainerSel);
					this.$loadingContainer .addClass('djangollery-loader');
				}
			},			
	
			/**
			 * Prepara el contenedor de los controles de navegacion
			 * @private
			 * @type Djangollery
			 */	
			setupControls: function() {
				var self = this;
				if (this.settings.controlsContainerSel) {
					this.$controlsContainer = $(this.settings.controlsContainerSel).empty();
					this.$controlsContainer.addClass('djangollery-controls');
					if (this.settings.renderSSControls) {
						if (this.settings.autoStart) {
							this.$controlsContainer
								.append(
									'<div class="djangollery-ss-controls"> \
										<a href="#pause" class="pause" title="'+this.settings.pauseLinkText+'">'+this.settings.pauseLinkText+'</a>\
									</div>');
						} else {
							this.$controlsContainer
								.append(
									'<div class="djangollery-ss-controls">\
										<a href="#play" class="play" title="'+this.settings.playLinkText+'">'+this.settings.playLinkText+'</a>\
									</div>');
						}
		
						this.$controlsContainer.find('div.djangollery-ss-controls a')
							.click(function(e) {
								self.toggleSlideshow();
								e.preventDefault();
								return false;
							});
					}
				
					if (this.settings.renderNavControls) {
						var $navControls = this.$controlsContainer
							.append(
								'<div class="djangollery-nav-controls">\
									<a class="djangollery-nav-controls-prev" rel="history" title="' +this.settings.prevLinkText+'">'
									+this.settings.prevLinkText
									+'</a>\
									<a class="djangollery-nav-controls-next" rel="history" title="'+this.settings.nextLinkText+'">'
									+this.settings.nextLinkText
									+'</a>\
								</div>')
							.find('div.djangollery-nav-controls a')
							.click(function(e) {
								clickHandler(e, self, this);
							});
					}
				}	
			},
	
			isPreloadComplete: false,

			preloadInit: function() {
				if (this.settings.preloadAhead == 0) return this;
				
				this.preloadStartIndex = this.currentIndex;
				var nextIndex = this.getNextIndex(this.preloadStartIndex);
				return this.preloadRecursive(this.preloadStartIndex, nextIndex);
			},
			
			preloadRelocate: function(index) {
				// By changing this startIndex, the current preload script will restart
				this.preloadStartIndex = index;
				return this;
			},

			preloadRecursive: function(startIndex, currentIndex) {
				// Check if startIndex has been relocated
				if (startIndex != this.preloadStartIndex) {
					var nextIndex = this.getNextIndex(this.preloadStartIndex);
					return this.preloadRecursive(this.preloadStartIndex, nextIndex);
				}

				var gallery = this;

				// Now check for preloadAhead count
				var preloadCount = currentIndex - startIndex;
				if (preloadCount < 0)
					preloadCount = this.data.length-1-startIndex+currentIndex;
				if (this.settings.preloadAhead >= 0 && preloadCount > this.settings.preloadAhead) {
					// Do this in order to keep checking for relocated start index
					setTimeout(function() { gallery.preloadRecursive(startIndex, currentIndex); }, 500);
					return this;
				}

				var imageData = this.data[currentIndex];
				if (!imageData)
					return this;

				// If already loaded, continue
				if (imageData.image)
					return this.preloadNext(startIndex, currentIndex); 
				
				// Preload the image
				var image = new Image();
				
				image.onload = function() {
					imageData.image = this;
					gallery.preloadNext(startIndex, currentIndex);
				};

				image.alt = imageData.title;
				image.src = imageData.slideUrl;

				return this;
			},
			
			preloadNext: function(startIndex, currentIndex) {
				var nextIndex = this.getNextIndex(currentIndex);
				if (nextIndex == startIndex) {
					this.isPreloadComplete = true;
				} else {
					// Use set timeout to free up thread
					var gallery = this;
					setTimeout(function() { gallery.preloadRecursive(startIndex, nextIndex); }, 100);
				}
				return this;
			},

			getNextIndex: function(index) {
				var nextIndex = index+1;
				if (nextIndex >= this.data.length)
					nextIndex = 0;
				return nextIndex;
			},
			
			getPrevIndex: function(index) {
				var prevIndex = index-1;
				if (prevIndex < 0)
					prevIndex = this.data.length-1;
				return prevIndex;
			},

			pause: function() {
				if (this.interval)
					this.toggleSlideshow();
				
				return this;
			},

			play: function() {
				if (!this.interval)
					this.toggleSlideshow();
				
				return this;
			},

			toggleSlideshow: function() {
				if (this.interval) {
					clearInterval(this.interval);
					this.interval = 0;
					
					if (this.$controlsContainer) {
						this.$controlsContainer
							.find('div.djangollery-ss-controls a').removeClass().addClass('play')
							.attr('title', this.settings.playLinkText)
							.attr('href', '#play')
							.html(this.settings.playLinkText);
					}
				} else {
					this.ssAdvance();

					var gallery = this;
					this.interval = setInterval(function() {
						gallery.ssAdvance();
					}, this.settings.delay);
					
					if (this.$controlsContainer) {
						this.$controlsContainer
							.find('div.djangollery-ss-controls a').removeClass().addClass('pause')
							.attr('title', this.settings.pauseLinkText)
							.attr('href', '#pause')
							.html(this.settings.pauseLinkText);
					}
				}

				return this;
			},

			ssAdvance: function() {
				var nextIndex = this.getNextIndex(this.currentIndex);
				var nextHash = this.data[nextIndex].hash;

				// Seems to be working on both FF and Safari
				if (this.settings.enableHistory)
					location.href = '#'+nextHash;
				else
					this.goTo(nextIndex);

				// IE we need to explicity call goto
				//if ($.browser.msie) {
				//	this.goto(nextIndex);
				//}

				return this;
			},

			goTo: function(index) {
				if (index < 0) 
					index = 0;
				else if (index >= this.data.length) 
					index = this.data.length-1;
				
				if (this.settings.onChange)
					this.settings.onChange(this.currentIndex, index);
				
				this.currentIndex = index;
				this.preloadRelocate(index);
				//
				return this.refresh();
			},
			
			refresh: function() {
				var imageData = this.data[this.currentIndex];
				if (!imageData)
					return this;
				
				// Flag we are transitioning
				var isTransitioning = true;

				var gallery = this;

				var transitionOutCallback = function() {
					// Flag that the transition has completed
					isTransitioning = false;

					// Update Controls
					if (gallery.$controlsContainer) {
						gallery.$controlsContainer
							.find('div.djangollery-nav-controls a.djangollery-nav-controls-prev')
							.attr('href', '#'+gallery.data[gallery.getPrevIndex(gallery.currentIndex)].hash)
							.end()
							.find('div.djangollery-nav-controls a.djangollery-nav-controls-next')
							.attr('href', '#'+gallery.data[gallery.getNextIndex(gallery.currentIndex)].hash);
					}

					var imageData = gallery.data[gallery.currentIndex];

					// Replace Caption
					if (gallery.$captionContainer) {
						gallery.$captionContainer.empty().append(imageData.caption);
					}

					if (imageData.image) {
						gallery.buildImage(imageData.image);
					} else {
						// Show loading container
						if (gallery.$loadingContainer) {
							gallery.$loadingContainer.show();
						}
					}
				};

				if (this.settings.onTransitionOut) {
					this.settings.onTransitionOut(transitionOutCallback);
				} else {
					this.$transitionContainers.hide();
					transitionOutCallback();
				}

				if (!imageData.image) {
					var image = new Image();
					
					// Wire up mainImage onload event
					image.onload = function() {
						imageData.image = this;

						if (!isTransitioning) {
							gallery.buildImage(imageData.image);
						}
					};

					// set alt and src
					image.alt = imageData.title;
					image.src = imageData.slideUrl;
				}

				// This causes the preloader (if still running) to relocate out from the currentIndex
				this.relocatePreload = true;

				return this.syncThumbs();
			},
			
			
			
			/**
             * Build the image preview.
             *
             * @name buildImage
             * @type undefined
             */
			buildImage: function(image) {
				if (this.$imageContainer) {
					this.$imageContainer.empty();

					var gallery = this;
					var nextIndex = this.getNextIndex(this.currentIndex);

					// Hide the loading conatiner
					if (this.$loadingContainer) {
						this.$loadingContainer.hide();
					}

					// Setup image
					this.$imageContainer
						.append('<span class="djangollery-image-wrapper"><a class="advance-link" rel="history" href="#'+this.data[nextIndex].hash+'" title="'+image.alt+'"></a></span>')
						.find('a')
						.append(image)
						.click(function(e) {
							clickHandler(e, gallery, this);
						});
				}

				if (this.settings.onTransitionIn)
					this.settings.onTransitionIn();
				else
					this.$transitionContainers.show();
				return this;
			},

			/**
             * Syncs the carousel's thumbs.
             *
             * @name syncThumbs
             * @type undefined
             */
			syncThumbs: function() {
				if (this.$thumbsContainer ) {
					// Remove existing selected class and add selected class to new thumb
					var $thumbs = this.$thumbsContainer.find('ul.djangollery-thumbs').children();
					$thumbs.filter('.djangollery-item-selected').removeClass('djangollery-item-selected');
					$thumbs.eq(this.currentIndex).addClass('djangollery-item-selected');
					if( this.last && this.currentIndex+1 >= this.last )
						this.scroll(this.currentIndex, false);
						//this.scroll(this.first + this.settings.scroll, false);						
					 else if( this.first && this.currentIndex < this.first )
						this.scroll(this.currentIndex, false);
						
				}
				return this;
			},

            /**
             * Setups the carousel.
             *
             * @name initCarousel
             * @type undefined
             */
            initCarousel: function() {
                this.first     = null;
                this.last      = null;
                this.prevFirst = null;
                this.prevLast  = null;
                this.animating = false;
                this.timer     = null;
                this.tail      = null;
                this.inTail    = false;

                if (this.locked)
                    return;

                this.list.css('left', this.pos(1) + 'px');
                var p = this.pos(this.settings.start);
                this.prevFirst = this.prevLast = null;
                this.animate(p, false);

                $(window).unbind('resize', this.funcResize).bind('resize', this.funcResize);
				console.log('initCarousel='+this.last)
            },
			
			 /**
	         * Sets the states of the prev/next buttons.
	         *
	         * @name buttons
	         * @type undefined
	        
	         */
	        buttons: function(n, p) {
	            if (n == undefined || n == null) {
	                var n = !this.locked && this.data.length !== 0 && 
						((this.settings.wrap && this.settings.wrap != 'first') ||  this.data.length != 0 || this.last < this.data.length );
	                if (!this.locked && 
						(!this.settings.wrap || this.settings.wrap == 'first') && this.data.length != 0 && this.last >= this.data.length )
	                    n = this.tail != null && !this.inTail;
	            }

	            if (p == undefined || p == null) {
	                var p = !this.locked && this.data.length !== 0 && ((this.settings.wrap && this.settings.wrap != 'last') || this.first > 1);
	                if (!this.locked && (!this.settings.wrap || this.settings.wrap == 'last') && this.data.length != 0 && this.first == 1)
	                    p = this.tail != null && this.inTail;
	            }

	            var self = this;

	            this.buttonNext[n ? 'bind' : 'unbind'](this.settings.buttonNextEvent, this.funcNext)[n ? 'removeClass' : 'addClass']('carousel-next-disabled').attr('disabled', n ? false : true);
	            this.buttonPrev[p ? 'bind' : 'unbind'](this.settings.buttonPrevEvent, this.funcPrev)[p ? 'removeClass' : 'addClass']('carousel-prev-disabled').attr('disabled', p ? false : true);

	            if (this.buttonNext.length > 0 && (this.buttonNext[0].carouselstate == undefined || this.buttonNext[0].carouselstate != n) && this.settings.buttonNextCallback != null) {
	                this.buttonNext.each(function() { self.settings.buttonNextCallback(self, this, n); });
	                this.buttonNext[0].carouselstate = n;
	            }

	            if (this.buttonPrev.length > 0 && (this.buttonPrev[0].carouselstate == undefined || this.buttonPrev[0].carouselstate != p) && this.settings.buttonPrevCallback != null) {
	                this.buttonPrev.each(function() { self.settings.buttonPrevCallback(self, this, p); });
	                this.buttonPrev[0].carouselstate = p;
	            }
	        },
            
		  /**
	         * Reloads the carousel and adjusts positions.
	         *
	         * @name reload
	         * @type undefined
	        
	         */
	        reload: function() {
				console.debug("reload");
	            if (this.tail != null && this.inTail)
	                this.list.css('left', $gl.intval(this.list.css('left')) + this.tail);

	            this.tail   = null;
	            this.inTail = false;

	            if (this.settings.visible != null) {
	                var self = this;
	                var di = Math.ceil(this.clipping() / this.settings.visible), wh = 0, lt = 0;
	                $('li', this.list).each(function(i) {
	                    wh += self.dimension(this,di);
	                    if (i + 1 < self.first)
	                        lt = wh;
	                });

	                this.list.css('width', wh + 'px');
	                this.list.css('left', -lt + 'px');
	            }

	            this.scroll(this.first, false);
	        },

            /**
             * Returns a jQuery object with list element for the given index.
             *
             * @name get
             * @type jQuery
             * @param Number i The index of the element.
            
             */
            get: function(i) {
                return $('.carousel-item-' + i, this.list);
            },
	     	/**
	         * Moves the carousel forwards.
	         *
	         * @name next
	         * @type undefined
	        
	         */
	        next: function() {
	            this.stopAuto();
	            if (this.tail != null && !this.inTail)
	                this.scrollTail(false);
	            else
	                this.scroll(((this.settings.wrap == 'both' || this.settings.wrap == 'last') && this.data.length != null && this.last == this.data.length) ? 1 : this.first + this.settings.scroll);
	        },

	        /**
	         * Moves the carousel backwards.
	         *
	         * @name prev
	         * @type undefined
	        
	         */
	        prev: function() {
	            this.stopAuto();

	            if (this.tail != null && this.inTail)
	                this.scrollTail(true);
	            else
	                this.scroll(((this.settings.wrap == 'both' || this.settings.wrap == 'first') && this.data.length != 0 && this.first == 1) ? this.data.length : this.first - this.settings.scroll);
	        },
	
			 /**
	         * Starts autoscrolling.
	         *
	         * @name auto
	         * @type undefined
	         * @param Number s Seconds to periodically autoscroll the content.
	        
	         */
	        startAuto: function(s) {
	            if (s != undefined)
	                this.settings.auto = s;

	            if (this.settings.auto == 0)
	                return this.stopAuto();

	            if (this.timer != null)
	                return;

	            var self = this;
	            this.timer = setTimeout(function() { self.next(); }, this.settings.auto * 1000);
	        },

	        /**
	         * Stops autoscrolling.
	         *
	         * @name stopAuto
	         * @type undefined
	        
	         */
	        stopAuto: function() {
	            if (this.timer == null)
	                return;

	            clearTimeout(this.timer);
	            this.timer = null;
	        },

			/**
	         * Scrolls the tail of the carousel.
	         *
	         * @name scrollTail
	         * @type undefined
	         * @param Bool b Whether scroll the tail back or forward.
	        
	         */
	        scrollTail: function(b) {
	            if (this.locked || this.animating || !this.tail)
	                return;

	            var pos  = $gl.intval(this.list.css('left'));

	            !b ? pos -= this.tail : pos += this.tail;
	            this.inTail = !b;

	            // Save for callbacks
	            this.prevFirst = this.first;
	            this.prevLast  = this.last;

	            this.animate(pos);
	        },
	
	        /**
	         * Scrolls the carousel to a certain position.
	         *
	         * @name scroll
	         * @type undefined
	         * @param Number i The index of the element to scoll to.
	         * @param Bool a Flag indicating whether to perform animation.
	        
	         */
	        scroll: function(i, a) {
	            if (this.locked || this.animating)
	                return;

	            this.animate(this.pos(i), a);
	        },
	        /**
	         * Prepares the carousel and return the position for a certian index.
	         *
	         * @name pos
	         * @type Number
	         * @param Number i The index of the element to scoll to.
	        
	         */
	        pos: function(i) {
	            if (this.locked || this.animating)
	                return;

	            if (this.settings.wrap != 'circular')
	                i = i < 1 ? 1 : (this.data.length && i > this.data.length ? this.data.length : i);

	            var back = this.first > i;
	            var pos  = $gl.intval(this.list.css('left'));

	            // Create placeholders, new list width/height
	            // and new list position
	            var f = this.settings.wrap != 'circular' && this.first <= 1 ? 1 : this.first;
	            var c = back ? this.get(f) : this.get(this.last);
	            var j = back ? f : f - 1;
	            var e = null, l = 0, p = false, d = 0;

	            while (back ? --j >= i : ++j < i) {
	                e = this.get(j);
	                p = !e.length;
	                if (e.length == 0) {
	                    e = this.create(j).addClass('carousel-item-placeholder');
	                    c[back ? 'before' : 'after' ](e);
	                }

	                c = e;
	                d = this.dimension(e);
					
	                if (p)
	                    l += d;

	                if (this.first != null && (this.settings.wrap == 'circular' || (j >= 1 && (this.data.length == 0 || j <= this.data.length))))
	                    pos = back ? pos + d : pos - d;
	            }

	            // Calculate visible items
	            var clipping = this.clipping();
	            var cache = [];
	            var visible = 0, j = i, v = 0;
	            var c = this.get(i - 1);

	            while (++visible) {
	                e = this.get(j);
	                p = !e.length;
	                if (e.length == 0) {
	                    e = this.create(j).addClass('carousel-item-placeholder');
	                    // This should only happen on a next scroll
	                    c.length == 0 ? this.list.prepend(e) : c[back ? 'before' : 'after' ](e);
	                }

	                c = e;
	                var d = this.dimension(e);
	                if (d == 0) {
	                    alert('carousel: No width/height set for items. This will cause an infinite loop. Aborting...');
	                    return 0;
	                }

	                if (this.settings.wrap != 'circular' && this.data.length !== null && j > this.data.length)
	                    cache.push(e);
	                else if (p)
	                    l += d;

	                v += d;

	                if (v >= clipping)
	                    break;

	                j++;
	            }
				

	             // Remove out-of-range placeholders
	            for (var x = 0; x < cache.length; x++)
	                cache[x].remove();

	            // Resize list
	            if (l > 0) {
					console.log(this.list)
	                this.list.css('width', this.list.data('dimension') + l + 'px');

	                if (back) {
	                    pos -= l;
	                    this.list.css('left', $gl.intval(this.list.css('left')) - l + 'px');
	                }
	            }

	            // Calculate first and last item
	            var last = i + visible - 1;
	            if (this.settings.wrap != 'circular' && this.data.length && last > this.data.length)
	                last = this.data.length;

	            if (j > last) {
	                visible = 0;
	                j = last;
	                v = 0;
	                while (++visible) {
	                    var e = this.get(j--);
	                    if (!e.length)
	                        break;
	                    v += this.dimension(e);
	                    if (v >= clipping)
	                        break;
	                }
	            }

	            var first = last - visible + 1;
	            if (this.settings.wrap != 'circular' && first < 1)
	                first = 1;

	            if (this.inTail && back) {
	                pos += this.tail;
	                this.inTail = false;
	            }

	            this.tail = null;
	            if (this.settings.wrap != 'circular' && last == this.data.length && (last - visible + 1) >= 1) {
	                var m = $gl.margin(this.get(last),  'marginRight' );
	                if ((v - m) > clipping)
	                    this.tail = v - clipping - m;
	            }

	            // Adjust position
	            while (i-- > first)
	                pos += this.dimension(this.get(i));

	            // Save visible item range
	            this.prevFirst = this.first;
	            this.prevLast  = this.last;
	            this.first     = first;
	            this.last      = last;
				console.log('pos='+this.last)
	            return pos;
	        },
            
			create: function(i) {
		            return this.format('<li></li>', i);		     
		    },
            
            format: function(e, i) {
                var $e = $(e).addClass('carousel-item').addClass('carousel-item-' + i);
                $e.attr('carouselindex', i);
                return $e;
            },

	        /**
	         * Animates the carousel to a certain position.
	         *
	         * @name animate
	         * @type undefined
	         * @param mixed p Position to scroll to.
	         * @param Bool a Flag indicating whether to perform animation
	         */
	        animate: function(p, a) {
	            if (this.locked || this.animating)
	                return;

	            this.animating = true;

	            var self = this;
	            var scrolled = function() {
	                self.animating = false;

	                if (p == 0)
	                    self.list.css('left',  0);

	                if (self.settings.wrap == 'both' || self.settings.wrap == 'last' || self.data.length == 0 || self.last < self.data.length)
	                    self.startAuto();

	                self.buttons();
	                self.notify('onAfterAnimation');
	            };

	            this.notify('onBeforeAnimation');

	            // Animate
	            if (!this.settings.animation || a == false) {
	                this.list.css('left', p + 'px');
	                scrolled();
	            } else {
	                var o = {'left': p};
	                this.list.animate(o, this.settings.animation, this.settings.easing, scrolled);
	            }
	        },
	
	        dimension: function(e, d) {
	            var el = e.jquery != undefined ? e[0] : e;
	            var old = el.offsetWidth + $gl.margin(el, 'marginLeft') + $gl.margin(el, 'marginRight');
	            if (d == undefined || old == d)
	                return old;

	            var w = d - $gl.margin(el, 'marginLeft') - $gl.margin(el, 'marginRight');
				
	            $(el).css('width', w + 'px');

	            return this.dimension(el);
	        },
	        
	        dimensionThumb: function (e) {
	        	 var el = e.jquery != undefined ? e[0] : e;
	        	 var result=$(el).data('dimension');
	        	 console.debug(result);
	        	 return result;	
	        },

	        clipping: function() {
	            return this.clip[0].offsetWidth - $gl.intval(this.clip.css('borderLeftWidth')) - $gl.intval(this.clip.css('borderRightWidth'));
	        },

	        index: function(i, s) {
	            if (s == undefined)
	                s = this.data.length;

	            return Math.round((((i-1) / s) - Math.floor((i-1) / s)) * s) + 1;
	        },
            
            notify: function(evt) {
                var state = this.prevFirst == null ? 'init' : (this.prevFirst < this.first ? 'next' : 'prev');

                // Load items
                this.callback('itemLoadCallback', evt, state);

                if (this.prevFirst !== this.first) {
                    this.callback('itemFirstInCallback', evt, state, this.first);
                    this.callback('itemFirstOutCallback', evt, state, this.prevFirst);
                }

                if (this.prevLast !== this.last) {
                    this.callback('itemLastInCallback', evt, state, this.last);
                    this.callback('itemLastOutCallback', evt, state, this.prevLast);
                }

                this.callback('itemVisibleInCallback', evt, state, this.first, this.last, this.prevFirst, this.prevLast);
                this.callback('itemVisibleOutCallback', evt, state, this.prevFirst, this.prevLast, this.first, this.last);
            },

            callback: function(cb, evt, state, i1, i2, i3, i4) {
                if (this.settings[cb] == undefined || (typeof this.settings[cb] != 'object' && evt != 'onAfterAnimation'))
                    return;

                var callback = typeof this.settings[cb] == 'object' ? this.settings[cb][evt] : this.settings[cb];

                if (!$.isFunction(callback))
                    return;

                var self = this;

                if (i1 === undefined)
                    callback(self, state, evt);
                else if (i2 === undefined)
                    this.get(i1).each(function() { callback(self, this, i1, state, evt); });
                else {
                    for (var i = i1; i <= i2; i++)
                        if (i !== null && !(i >= i3 && i <= i4))
                            this.get(i).each(function() { callback(self, this, i, state, evt); });
                }
            }
	
		});
        
        
        $gl.extend({        
            margin: function(e, p) {
	            if (!e)
	                return 0;

	            var el = e.jquery != undefined ? e[0] : e;

	            if (p == 'marginRight' && $.browser.safari) {
	                var old = {'display': 'block', 'float': 'none', 'width': 'auto'}, oWidth, oWidth2;

	                $.swap(el, old, function() { oWidth = el.offsetWidth; });

	                old['marginRight'] = 0;
	                $.swap(el, old, function() { oWidth2 = el.offsetWidth; });

	                return oWidth2 - oWidth;
	            }

	            return $gl.intval($.css(el, p));
	        },

	        intval: function(v) {
	            v = parseInt(v,10);
	            return isNaN(v) ? 0 : v;
	        }
        
        }); 
    
})(jQuery);
